package cn.donting.plugin.spring.boot.starter;

import cn.donting.plugin.spring.boot.starter.exception.PluginRuntimeException;
import cn.donting.plugin.spring.boot.starter.plugin.autoconfiguration.web.PluginServletContext;
import cn.donting.plugin.spring.boot.starter.utile.SpringApplicationUtil;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfigurationImportSelector;
import org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration;
import org.springframework.boot.env.PropertiesPropertySourceLoader;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;


/**
 * 普通jar插件包装类
 *
 * @author donting
 * 2020-04-17 下午1:45
 */
@Getter
@Slf4j
public class PluginWrapper extends AnnotationConfigApplicationContext implements PluginEven {
    /**
     * 路径 加载插件的路径
     */
    private String path;

    /**
     * 插件数据 路径
     */
    private String dataPath;

    /**
     * 类加载器
     */
    private ClassLoader classLoader;

    private Plugin plug;
    /**
     * 插件入口
     */
    private Class<?> plugClass;
    /**
     * spring 容器
     */
    private PluginDispatcherServlet dispatcherServlet;

    private List<PluginEven> evenList = new ArrayList<>();

    private Start start = Start.STOP;

    private enum Start {
        STOP, STARTED
    }

    public PluginWrapper(String path, ClassLoader classLoader, Plugin plug, Class<?> plugClass) {
        this.classLoader = classLoader;
        this.plug = plug;
        this.path = path;
        this.plugClass = plugClass;
        super.setClassLoader(classLoader);
    }

    /**
     * 初始化DispatcherServlet
     */
    private void initDispatcherServlet() {
        dispatcherServlet = new PluginDispatcherServlet(this);
    }

    /**
     * spring 容器初始化
     */
    private void plugApplicationContextInit() {
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(classLoader);
        super.setClassLoader(classLoader);
        super.registerBean("pluginMain", plugClass);
        super.register(PluginServletContext.class);
        //开启Configuration 配合AutoConfigurationImportSelector
        super.register(AutoConfigurationImportSelector.class);
        super.register(ConfigurationPropertiesAutoConfiguration.class);
//        super.register(PluginMvcAutoConfiguration.class);
        super.scan("cn.donting.plugin.spring.boot.starter.plugin.autoconfiguration");


        //扫描包 启动类所在的包，plug自动配置的包
        super.scan(plugClass.getPackage().getName());
        //配置文件
        setPropertySources();

        //刷新 上下文
        super.refresh();
        //初始化 DispatcherServlet
        initDispatcherServlet();
        //获取插件事件
        Collection<PluginEven> values = super.getBeansOfType(PluginEven.class).values();
        //插件时间排序
        evenList = SpringApplicationUtil.orderSort(new ArrayList<>(values));
        super.start();
        Environment bean = super.getBean(Environment.class);
        Thread.currentThread().setContextClassLoader(contextClassLoader);
    }

    @Override
    public void install() {
        synchronized (start) {
            log.info("安装插件：{}:{}", plug.name(), plug.id());
            start();
            for (PluginEven pluginEven : evenList) {
                pluginEven.install();
            }
        }
    }

    @Override
    public void uninstall() {
        synchronized (start) {
            log.info("卸载插件：{}:{}", plug.name(), plug.id());
            for (PluginEven pluginEven : evenList) {
                pluginEven.uninstall();
            }
            super.stop();
        }
    }

    @Override
    public void stop() {
        synchronized (start) {
            log.info("停止插件：{}:{}", plug.name(), plug.id());
            for (PluginEven pluginEven : evenList) {
                pluginEven.stop();
            }
            super.stop();
        }
    }

    /**
     * 设置插件的数据路径
     *
     * @param path path
     */
    public void setDataPath(String path) {
        if (this.dataPath == null) {
            this.dataPath = path;
        } else {
            log.warn("dataPath is set");
        }
    }

    /**
     * 获取数据 路径
     *
     * @return path
     */
    public String getDataPath() {
        return dataPath;
    }

    @Override
    public void start() {
        synchronized (start) {
            log.info("启动插件：name:{} id:{}", plug.name(), plug.id());
            if (start == Start.STARTED) {
                log.warn("插件已启动");
                return;
            }
            start = Start.STARTED;
            plugApplicationContextInit();
            for (PluginEven pluginEven : evenList) {
                pluginEven.start();
            }
        }
    }

    /**
     * 配置文件设置
     */
    private void setPropertySources() {
        MutablePropertySources mutablePropertySources = getEnvironment().getPropertySources();
        PropertiesPropertySourceLoader propertiesPropertySourceLoader = new PropertiesPropertySourceLoader();
        try {
            List<PropertySource<?>> application = propertiesPropertySourceLoader.load("application.properties", getResource("application.properties"));
            if (application.size() == 0) {
                return;
            }
            Object active = application.get(0).getProperty("spring.profiles.active");
            if (active != null) {
                String[] actives = active.toString().split(",");
                getEnvironment().setActiveProfiles(actives);
                for (int i = actives.length - 1; i >= 0; i--) {
                    //后加载
                    try {
                        List<PropertySource<?>> propertySources = propertiesPropertySourceLoader.load("application-" + actives[i] + ".properties",
                                getResource("application-" + actives[i] + ".properties"));
                        if (propertySources.size() > 0) {
                            mutablePropertySources.addLast(propertySources.get(0));
                        } else {
                            log.warn("application-" + actives[i] + ".properties is not found");
                        }
                    } catch (Exception ex) {
                        log.warn(ex.getMessage());
                    }

                }
            }
            mutablePropertySources.addLast(application.get(0));
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            throw new PluginRuntimeException(e.getMessage(), e);
        }

    }


}
